require("wx")
require("editor.Tree")
require("editor.NodeConfig")
require("editor.NodeColor")
require("editor.Templates")
require("editor.Res")

if not pcall(require,"editor.EditorSetting") then 
	setting={
		["updatelib"]=false,
		["projpath"]="",
		["windowed"]=true,
		["resx"]=800,
		["resy"]=600,
	}
end

local xmlResource=nil
local frame=nil
local projectTree=nil
local rootNode=nil
local curNode=nil
local logWindow=nil
local logHtml=''
local attrLabel={}
local attrCombo={}
local attrButton={}
local typeNameLabel=nil
local lineNumText=nil
local Id2Type={}
local dialogNewProj=nil
local listTemplate=nil
local filePickerNewProj=nil
local dialogEditText=nil
local editAttrLabel=nil
local editAttrText=nil
local attrIndex=nil
local dialogSetting=nil
local resXText=nil
local resYText=nil
local windowedCheckBox=nil
local updateLibCheckBox=nil
local dialogSoundEffect=nil
local soundListBox=nil
local dialogType=nil
local typeListBox=nil
local dialogImage=nil
local imageListBox=nil
local imagePrevPanel=nil
local dialogInputParameter=nil
local paramNameLabel={}
local paramText={}
local dialogSelectEnemyStyle=nil
local selectEnemyStyleButton={}

local treeImageList=nil

local bitmapBackground=nil

local curProjFile=nil
local curProjDir=nil

local isDebug=false
local debugNode=nil
local scDebugNode=nil

function MakeFullPath(path)
	local fn=wx.wxFileName(path)
	if not fn:IsAbsolute() then fn:MakeAbsolute(curProjDir) return fn:GetFullPath()
	else return fn:GetFullPath() end
end

function FileExists(path)
	local fn=wx.wxFileName(path)
	if not fn:IsAbsolute() then fn:MakeAbsolute(curProjDir) return fn:FileExists()
	else return fn:FileExists() end
end

local Clip

function GetClipboard()
	local cp=wx.wxClipboard.Get()
	local text_data=wx.wxTextDataObject("")
	if not cp:Open() then return end
	local ret=cp:GetData(text_data)
	cp:Close()
	if not ret then return false
	else return text_data:GetText() end
end

function SetClipboard(s)
	local cp=wx.wxClipboard.Get()
	local text_data=wx.wxTextDataObject(s)
	if not cp:Open() then return end
	local ret=cp:SetData(text_data)
	cp:Flush()
	cp:Close()
	return ret
end

local insertPos="after"

local treeShot={}
local treeShotPos=0
local savedPos=0

local lineNum=0

outputName=""

cwd=wx.wxFileName(arg[0]):GetPath(wx.wxPATH_GET_VOLUME)

local function ID(s)
	return xmlResource.GetXRCID(s)
end

local function IsValid(node)
	if node==nil then return false
	else
		if Tree.data[node:GetValue()] then return true else return false end
	end
end

local function IsRoot(node)
	if node==nil then return false
	else return node:GetValue()==rootNode:GetValue() end
end

local function OutputLog(msg,icon)
	logHtml=string.format('%s<p><img src="editor/images/%s.png">%s</p>\n',logHtml,icon,msg)
	logWindow:SetPage(logHtml)
	logWindow:Scroll(-1,65536)
end
local function ClearLog(msg,icon)
	logHtml=''
	logWindow:SetPage(logHtml)
	logWindow:Scroll(-1,0)
end

local ancStack={}
local function CheckAnc(data)
	local ret
	local anc=nodeType[data.type].needancestor
	if anc then
		ret=false
		for _,v in ipairs(ancStack) do
			if anc[v] then ret=true end
		end
	else ret=true end
	if not ret then
		local needed={}
		for k,v in pairs(anc) do if type(k)=='string' then table.insert(needed,k) end end
		OutputLog(string.format('%q need ancestor: %s',data.type,table.concat(needed,'/')),"error")
		return false
	end
	local anc=nodeType[data.type].forbidancestor
	if anc then
		for _,v in ipairs(ancStack) do
			if anc[v] then OutputLog(string.format('%q forbid ancestor: %s',data.type,v),"error") return false end
		end
	end
	if data.child then
		table.insert(ancStack,data.type)
		for _,child in ipairs(data.child) do
			ret=(ret and CheckAnc(child))
		end
		ancStack[#ancStack]=nil
	end
	return ret
end

function main()
	xmlResource=wx.wxXmlResource()
	xmlResource:InitAllHandlers()
	xmlResource:Load("editor/LuaSTG Editor.xrc")
	--
	frame=wx.wxFrame()
	xmlResource:LoadFrame(frame,wx.NULL,"LuaSTG Editor")
	projectTree=frame:FindWindow(ID("LeftTree")):DynamicCast("wxTreeCtrl")
	logWindow=frame:FindWindow(ID("LogWindow")):DynamicCast("wxHtmlWindow")
	for i=1,15 do
		attrLabel[i]=frame:FindWindow(ID("AttrLabel"..i)):DynamicCast("wxStaticText")
		attrCombo[i]=frame:FindWindow(ID("AttrCombo"..i)):DynamicCast("wxComboBox")
		attrButton[i]=frame:FindWindow(ID("AttrButton"..i)):DynamicCast("wxButton")
	end
	typeNameLabel=frame:FindWindow(ID("TypeNameLabel")):DynamicCast("wxStaticText")
	lineNumText=frame:FindWindow(ID("LineNumText")):DynamicCast("wxTextCtrl")
	treeImageList=wx.wxImageList(16,16,false)
	local i=0
	for k,v in pairs(nodeType) do
		Id2Type[ID("Insert_"..k)]=v
		local btn=frame:FindWindow(ID("Insert_"..k))
		if btn then btn:SetToolTip(nodeType[k].disptype) end
		treeImageList:Add(wx.wxBitmap("editor\\images\\16x16\\"..k..".png",wx.wxBITMAP_TYPE_PNG))
		Tree.imageIndex[k]=i
		i=i+1
	end
	projectTree:AssignImageList(treeImageList)
	rootNode=projectTree:AddRoot("project",Tree.imageIndex.folder)
	--
	bitmapBackground=wx.wxBitmap("editor\\images\\imagebackground.png")
	--
	dialogNewProj=wx.wxDialog()
	xmlResource:LoadDialog(dialogNewProj,wx.NULL,"New Project")
	listTemplate=dialogNewProj:FindWindow(ID("ListTemplate")):DynamicCast("wxListBox")
	filePickerNewProj=dialogNewProj:FindWindow(ID("FilePickerNewProj")):DynamicCast("wxFilePickerCtrl")
	for i=1,#templates do
		listTemplate:Append(templates[i][1])
	end
	listTemplate:SetSelection(0)
	--
	dialogEditText=wx.wxDialog()
	xmlResource:LoadDialog(dialogEditText,wx.NULL,"Edit Text")
	editAttrLabel=dialogEditText:FindWindow(ID("EditAttrLabel")):DynamicCast("wxStaticText")
	editAttrText=dialogEditText:FindWindow(ID("EditAttrText")):DynamicCast("wxTextCtrl")
	--
	dialogSetting=wx.wxDialog()
	xmlResource:LoadDialog(dialogSetting,wx.NULL,"Setting")
	resXText=dialogSetting:FindWindow(ID("ResXText")):DynamicCast("wxTextCtrl")
	resYText=dialogSetting:FindWindow(ID("ResYText")):DynamicCast("wxTextCtrl")
	windowedCheckBox=dialogSetting:FindWindow(ID("WindowedCheckBox")):DynamicCast("wxCheckBox")
	updateLibCheckBox=dialogSetting:FindWindow(ID("UpdateLibCheckBox")):DynamicCast("wxCheckBox")
	--
	dialogSoundEffect=wx.wxDialog()
	xmlResource:LoadDialog(dialogSoundEffect,wx.NULL,"Select Sound Effect")
	soundListBox=dialogSoundEffect:FindWindow(ID("SoundListBox")):DynamicCast("wxListBox")
	--
	dialogType=wx.wxDialog()
	xmlResource:LoadDialog(dialogType,wx.NULL,"Select Type")
	typeListBox=dialogType:FindWindow(ID("TypeListBox")):DynamicCast("wxListBox")
	--
	dialogImage=wx.wxDialog()
	xmlResource:LoadDialog(dialogImage,wx.NULL,"Select Image")
	imageListBox=dialogImage:FindWindow(ID("ImageListBox")):DynamicCast("wxListBox")
	imagePrevPanel=dialogImage:FindWindow(ID("ImagePrevPanel")):DynamicCast("wxPanel")
	--
	dialogInputParameter=wx.wxDialog()
	xmlResource:LoadDialog(dialogInputParameter,wx.NULL,"Input Parameter")
	for i=1,16 do
		paramNameLabel[i]=dialogInputParameter:FindWindow(ID("ParamNameLabel"..i)):DynamicCast("wxStaticText")
		paramText[i]=dialogInputParameter:FindWindow(ID("ParamText"..i)):DynamicCast("wxTextCtrl")
	end
	--
	dialogSelectEnemyStyle=wx.wxDialog()
	xmlResource:LoadDialog(dialogSelectEnemyStyle,wx.NULL,"Select Enemy Style")
	for i=1,26 do
		selectEnemyStyleButton[i]=dialogSelectEnemyStyle:FindWindow(ID("Style"..i)):DynamicCast("wxBitmapButton")
	end
	--
	local picker={sound=dialogSoundEffect,image=dialogImage,selecttype=dialogType,param=dialogInputParameter,selectenemystyle=dialogSelectEnemyStyle}
	--
	local fileMenu=wx.wxMenu()
	fileMenu:Append(wx.wxID_NEW,"&New...")
	fileMenu:Append(wx.wxID_OPEN,"&Open...")
	fileMenu:Append(wx.wxID_SAVE,"&Save")
	fileMenu:Append(wx.wxID_SAVEAS,"Save &As...")
	fileMenu:Append(wx.wxID_CLOSE,"&Close")
	fileMenu:Append(wx.wxID_EXIT,"E&xit")

	local editMenu=wx.wxMenu()
	editMenu:Append(wx.wxID_UNDO,"&Undo")
	editMenu:Append(wx.wxID_REDO,"&Redo")
	editMenu:Append(wx.wxID_DELETE,"&Delete")
	editMenu:Append(wx.wxID_COPY,"&Copy")
	editMenu:Append(wx.wxID_CUT,"Cu&t")
	editMenu:Append(wx.wxID_PASTE,"&Paste")
	
	local helpMenu=wx.wxMenu()
	helpMenu:Append(wx.wxID_ABOUT,"&About")

	local menuBar = wx.wxMenuBar()
	menuBar:Append(fileMenu, "&File")
	menuBar:Append(editMenu, "&Edit")
	menuBar:Append(helpMenu, "&Help")

	frame:SetMenuBar(menuBar)
	--
	local function SetCurProjFile(s)
		curProjFile=s
		if s==nil then
			frame:SetTitle("LuaSTG Editor")
			curProjDir=nil
			ClearLog()
		else
			frame:SetTitle(curProjFile.." - LuaSTG Editor")
			curProjDir=wx.wxFileName(curProjFile):GetPath(wx.wxPATH_GET_VOLUME)
			setting.projpath=curProjDir
			local f=io.open("editor\\EditorSetting.lua","w")
			f:write("setting="..Tree.Serialize(setting))
			f:close()
			OutputLog(string.format("current project file: %s",wx.wxFileName(curProjFile):GetName()),"Info")
		end
	end
	local function SaveToFile(file)
		local f,msg=io.open(file,"w")
		if f==nil then return msg end
		local tmp={}
		for node in Tree.Children(projectTree,rootNode) do
			table.insert(tmp,Tree.Ctrl2Data(projectTree,node))
		end
		f:write(Tree.Serialize(tmp))
		f:close()
	end
	local function LoadFromFile(file,ignore_setting)
		local f,msg=io.open(file,"r")
		if f==nil then return msg end
		local tmp=Tree.DeSerialize(f:read("*a"))
		for i=1,#tmp do
			if not ignore_setting or tmp[i]["type"]~="setting" then Tree.Data2Ctrl(projectTree,rootNode,-1,tmp[i]) end
		end
		f:close()
	end
	function ApplyDepth(s,depth)
		local ret=string.gsub(s,"\n","\n"..string.rep(' ',depth*4))
		if string.sub(s,-1)=="\n" then ret=string.sub(ret,1,-1-depth*4) end
		return string.rep(' ',depth*4)..ret
	end
	function CompileToFile(f,node,depth)
		local data=Tree.data[node:GetValue()]
		local check=nodeType[data.type].check
		local head=nodeType[data.type].tohead
		local foot=nodeType[data.type].tofoot
		local msg
		for i,v in ipairs(data.attr) do
			local checkattr=nodeType[data.type][i][3]
			if checkattr then
				msg=checkattr(v)
				if msg~=nil then
					projectTree:SelectItem(node)
					attrCombo[i]:SetFocus()
					return string.format("Attribute %q is invalid: %s",nodeType[data.type][i][1],msg)
				end
			end
		end
		if check then
			msg=check(data)
			if msg~=nil then
				projectTree:SelectItem(node)
				return msg
			end
		end
		if debugNode and debugNode[2]==node:GetValue() then f:write('end\n') end
		if head then f:write(ApplyDepth(head(data),depth)) end
		for child in Tree.Children(projectTree,node) do
			msg=CompileToFile(f,child,depth+(nodeType[data.type].depth or 1))
			if msg~=nil then return msg end
		end
		if foot then f:write(ApplyDepth(foot(data),depth)) end
		if debugNode and debugNode[1]==node:GetValue() then f:write('if false then\n') end
	end
	local function TreeShotUpdate()
		local tmp={}
		for node in Tree.Children(projectTree,rootNode) do
			table.insert(tmp,Tree.Ctrl2Data(projectTree,node))
		end
		treeShotPos=treeShotPos+1
		treeShot[treeShotPos]=tmp
		for i=treeShotPos+1,#treeShot do treeShot[i]=nil end
	end
	local function SubmitAttr()
		local changed=false
		if IsValid(curNode) then
			local data=Tree.data[curNode:GetValue()]
			for i=1,#(data.attr) do
				if data.attr[i]~=attrCombo[i]:GetValue() then
					changed=true
					data.attr[i]=attrCombo[i]:GetValue()
				end
			end
			projectTree:SetItemText(curNode,(nodeType[data.type].totext)(data))
		end
		if changed then TreeShotUpdate() end
		return changed
	end
	local function InsertNode(tree,node,data)
		if not curProjFile then return end
		tree:SetFocus()
		local parent,pos,ret
		if insertPos=="child" or IsRoot(node) then
			parent=node pos=-1
		elseif insertPos=="after" then
			parent=tree:GetItemParent(node) pos=node
		else
			parent=tree:GetItemParent(node) pos=tree:GetPrevSibling(node)
		end
		--
		local ptype
		if IsValid(parent) then ptype=Tree.data[parent:GetValue()]["type"] else ptype="root" end
		local ctype=data.type
		if ptype~="root" then
			if nodeType[ptype].allowchild and not nodeType[ptype].allowchild[ctype] then OutputLog(string.format('can not insert %q as child of %q',ctype,ptype),"Error") return end
			if nodeType[ptype].forbidchild and nodeType[ptype].forbidchild[ctype] then OutputLog(string.format('can not insert %q as child of %q',ctype,ptype),"Error") return end
		end
		if nodeType[ctype].allowparent and not nodeType[ctype].allowparent[ptype] then OutputLog(string.format('can not insert %q as child of %q',ctype,ptype),"Error") return end
		if nodeType[ctype].forbidparent and nodeType[ctype].forbidparent[ptype] then OutputLog(string.format('can not insert %q as child of %q',ctype,ptype),"Error") return end
		--
		ancStack={}
		local pnode=parent
		while IsValid(pnode) do
			table.insert(ancStack,Tree.data[pnode:GetValue()]["type"])
			pnode=tree:GetItemParent(pnode)
		end
		if not CheckAnc(data) then return end
		--
		ret=Tree.Data2Ctrl(tree,parent,pos,data)
		tree:Expand(parent)
		tree:SelectItem(ret)
		TreeShotUpdate()
		--
		return ret
	end
	local function SaveProj(event)
		SubmitAttr()
		if curProjFile then
			local msg=SaveToFile(curProjFile)
			if msg~=nil then OutputLog(msg,"Error") return msg end
			savedPos=treeShotPos
		end
	end
	local function SaveProjAs(event)
		SubmitAttr()
		if curProjFile then
			local fd=wx.wxFileDialog(frame,"Save project",setting.projpath,"","LuaSTG project (*.luastg)|*.luastg",wx.wxFD_SAVE+wx.wxFD_OVERWRITE_PROMPT)
			if fd:ShowModal()==wx.wxID_OK then
				local fileName=fd:GetPath()
				local msg=SaveToFile(fileName)
				if msg==nil then
					SetCurProjFile(fileName)
					savedPos=treeShotPos
				else
					OutputLog(msg,"Error")
					return msg
				end
			end
		end
	end
	local function CloseProj(event)
		if curProjFile then
			local answer
			if savedPos~=treeShotPos then
				answer=wx.wxMessageBox('Save file "'..curProjFile..'" ?',"Save",wx.wxYES_NO+wx.wxCANCEL)
			else answer=wx.wxNO end
			if answer==wx.wxYES then
				local msg=SaveProj(event)
				if msg==nil then
					projectTree:DeleteChildren(rootNode)
					Tree.data={}
					treeShot={}
					treeShotPos=0
					savedPos=0
					SetCurProjFile(nil)
				else return msg end
			elseif answer==wx.wxNO then
				projectTree:DeleteChildren(rootNode)
				Tree.data={}
				treeShot={}
				treeShotPos=0
				savedPos=0
				SetCurProjFile(nil)
			elseif answer==wx.wxCANCEL then
				return "cancelled by user"
			end
		end
	end
	local function OpenProj(event)
		if curProjFile~=nil then
			local msg=CloseProj(event)
			if msg~=nil then return msg end
		end
		local fd=wx.wxFileDialog(frame,"Open project",setting.projpath,"","LuaSTG project (*.luastg)|*.luastg",wx.wxFD_FILE_MUST_EXIST+wx.wxFD_OPEN)
		if fd:ShowModal()==wx.wxID_OK then
			local fileName=fd:GetPath()
			local msg=LoadFromFile(fileName)
			if msg==nil then
				SetCurProjFile(fileName)
				projectTree:Expand(rootNode)
				TreeShotUpdate()
				savedPos=1
				projectTree:SetFocus()
			else
				OutputLog(msg,"Error")
				return msg
			end
		end
	end
	local function NewProj(event)
		if CloseProj(event)==nil then
			frame:Enable(false)
			dialogNewProj:Show(true)
		end
	end
	local _def_node_type={enemydefine=true,bulletdefine=true,objectdefine=true,laserdefine=true}
	local _init_node_type={enemyinit=true,bulletinit=true,objectinit=true,laserinit=true}
	local function CalcParamNumAll(node)
		if _def_node_type[node.type] then
			for k,v in pairs(node.child) do
				if _init_node_type[v.type] then
					paramNumDict[node.attr[1]]=CalcParamNum(v.attr[1])
					break
				end
			end
		elseif node.type=='folder' then
			for k,v in pairs(node.child) do
				CalcParamNumAll(v)
			end
		end
	end
	local function PackProj(event)
		if curProjFile then
			SubmitAttr()
			checkSoundName={}
			checkImageName={}
			checkAniName={}
			checkParName={}
			checkBgmName={}
			checkAnonymous={}
			checkResFile={}
			checkClassName={}
			watchDict={}
			paramNumDict={}
			for k,v in pairs(treeShot[treeShotPos]) do
				CalcParamNumAll(v)
			end
			for key,wdata in pairs(Tree.watch) do
				watchDict[key]={}
				if key~='image' then
					for item,_ in pairs(wdata) do
						watchDict[key][Tree.data[item].attr[1]]=true
					end
				end
			end
			watchDict.imageonly={}
			for k,v in pairs(Tree.watch.image) do
				if Tree.data[k]["type"]=='loadimage' then
					watchDict.image['image:'..Tree.data[k].attr[2]]=true
					watchDict.imageonly['image:'..Tree.data[k].attr[2]]=true
				elseif Tree.data[k]["type"]=='loadani' then
					watchDict.image['ani:'..Tree.data[k].attr[2]]=true
				elseif Tree.data[k]["type"]=='loadparticle' then
					watchDict.image['particle:'..Tree.data[k].attr[2]]=true
				end
			end
			for child in Tree.Children(projectTree,rootNode) do
				local data=Tree.data[child:GetValue()]
				if data.type=='setting' then
					outputName=data.attr[1]
					break
				end
			end
			if outputName=='unnamed' then outputName=wx.wxFileName(curProjFile):GetName() end
			local f,msg=io.open("editor\\tmp\\_pack_res.bat","w")
			if f==nil then OutputLog(msg,"Error") return msg end
			f:write('del "mod\\'..outputName..'.zip"\n')
			f:write('zip -j "mod\\'..outputName..'.zip" editor\\root.lua editor\\tmp\\_editor_output.lua\n')
			f:close()
			local f,msg=io.open("editor\\tmp\\_editor_output.lua","w")
			if f==nil then OutputLog(msg,"Error") return msg end
			if isDebug and curNode then
				local taskNode=projectTree:GetItemParent(curNode)
				if Tree.data[taskNode:GetValue()]['type']=='stagetask' then
					local stageNode,groupNode
					stageNode=projectTree:GetItemParent(taskNode)
					groupNode=projectTree:GetItemParent(stageNode)
					f:write("-- Debug codes\n")
					f:write(string.format("_debug_stage_name='%s@%s'\n",Tree.data[stageNode:GetValue()].attr[1],Tree.data[groupNode:GetValue()].attr[1]))
					f:write("Include'THlib\\\\UI\\\\debugger.lua'\n\n")
					firstNode=projectTree:GetFirstChild(taskNode)
					if firstNode:GetValue()~=curNode:GetValue() then
						debugNode={firstNode:GetValue(),curNode:GetValue()}
					end
				else
					return "must debug from direct child node of stagetask node"
				end
			end
			for child in Tree.Children(projectTree,rootNode) do
				msg=CompileToFile(f,child,0)
				if msg then
					f:close()
					projectTree:SetFocus()
					return msg
				end
			end
			if scDebugNode then
				f:write("Include'THlib\\\\UI\\\\scdebugger.lua'\n\n")
				className=Tree.data[projectTree:GetItemParent(scDebugNode):GetValue()].attr[1]
				f:write(string.format("_boss_class_name=%q",className))
				f:write(string.format("_editor_class[%q].cards={}",className))
				f:write(string.format("table.insert(_editor_class[%q].cards,boss.move.New(0,144,60,MOVE_NORMAL))",className))
				CompileToFile(f,scDebugNode,0)
			end
			f:close()
			os.execute()
			os.execute('editor\\tmp\\_pack_res.bat > pack_log.txt')
		end
	end
	local function OnQuit(event)
		local msg=CloseProj(event)
		if msg==nil then
			event:Skip()
			wx.wxExit()
		end
	end
	local function ToolUndo(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if treeShot[treeShotPos-1] then
				treeShotPos=treeShotPos-1
				projectTree:DeleteChildren(rootNode)
				Tree.data={}
				for i=1,#(treeShot[treeShotPos]) do
					Tree.Data2Ctrl(projectTree,rootNode,-1,treeShot[treeShotPos][i])
				end
			end
		end
	end
	local function ToolRedo(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if treeShot[treeShotPos+1] then
				treeShotPos=treeShotPos+1
				projectTree:DeleteChildren(rootNode)
				Tree.data={}
				for i=1,#(treeShot[treeShotPos]) do
					Tree.Data2Ctrl(projectTree,rootNode,-1,treeShot[treeShotPos][i])
				end
			end
		end
	end
	local function ToolDelete(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if IsValid(curNode) and not nodeType[Tree.data[curNode:GetValue()]["type"]].forbiddelete then
				projectTree:Delete(curNode)
				TreeShotUpdate()
			end
		end
	end
	local function ToolCopy(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if IsValid(curNode) then
				SetClipboard("\001LuaSTG"..Tree.Serialize(Tree.Ctrl2Data(projectTree,curNode)))
			end
		end
	end
	local function ToolCut(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if IsValid(curNode) and not nodeType[Tree.data[curNode:GetValue()]["type"]].forbiddelete then
				SetClipboard("\001LuaSTG"..Tree.Serialize(Tree.Ctrl2Data(projectTree,curNode)))
				projectTree:Delete(curNode)
				TreeShotUpdate()
			end
		end
	end
	local function ToolPaste(event)
		if frame:FindFocus():GetId()==projectTree:GetId() then
			if IsValid(curNode) or IsRoot(curNode) then
				local cp=GetClipboard()
				if cp and string.sub(cp,1,7)=="\001LuaSTG" then
					InsertNode(projectTree,curNode,Tree.DeSerialize(string.sub(cp,8,-1)))
				end
			end
		end
	end
	--
	frame:Connect(wx.wxID_NEW,wx.wxEVT_COMMAND_MENU_SELECTED,NewProj)
	frame:Connect(wx.wxID_CLOSE,wx.wxEVT_COMMAND_MENU_SELECTED,CloseProj)
	frame:Connect(wx.wxID_SAVEAS,wx.wxEVT_COMMAND_MENU_SELECTED,SaveProjAs)
	frame:Connect(wx.wxID_OPEN,wx.wxEVT_COMMAND_MENU_SELECTED,OpenProj)
	frame:Connect(wx.wxID_SAVE,wx.wxEVT_COMMAND_MENU_SELECTED,SaveProj)
	frame:Connect(wx.wxID_EXIT,wx.wxEVT_COMMAND_MENU_SELECTED,function(event) frame:Close(true) end)
	frame:Connect(wx.wxEVT_CLOSE_WINDOW,OnQuit)
	frame:Connect(wx.wxID_UNDO,wx.wxEVT_COMMAND_MENU_SELECTED,ToolUndo)
	frame:Connect(wx.wxID_REDO,wx.wxEVT_COMMAND_MENU_SELECTED,ToolRedo)
	frame:Connect(wx.wxID_DELETE,wx.wxEVT_COMMAND_MENU_SELECTED,ToolDelete)
	frame:Connect(wx.wxID_COPY,wx.wxEVT_COMMAND_MENU_SELECTED,ToolCopy)
	frame:Connect(wx.wxID_CUT,wx.wxEVT_COMMAND_MENU_SELECTED,ToolCut)
	frame:Connect(wx.wxID_PASTE,wx.wxEVT_COMMAND_MENU_SELECTED,ToolPaste)
	--
	local accelTable=wx.wxAcceleratorTable({
        {wx.wxACCEL_CTRL,string.byte('N'),wx.wxID_NEW},
		{wx.wxACCEL_CTRL,string.byte('O'),wx.wxID_OPEN},
		{wx.wxACCEL_CTRL,string.byte('S'),wx.wxID_SAVE},
		{wx.wxACCEL_CTRL+wx.wxACCEL_SHIFT,string.byte('S'),wx.wxID_SAVEAS},
		{wx.wxACCEL_CTRL,string.byte('W'),wx.wxID_CLOSE},
		{wx.wxACCEL_CTRL,string.byte('Z'),wx.wxID_UNDO},
		{wx.wxACCEL_CTRL,string.byte('Y'),wx.wxID_REDO},
		{wx.wxACCEL_NORMAL,wx.WXK_DELETE,wx.wxID_DELETE},
		{wx.wxACCEL_CTRL,string.byte('C'),wx.wxID_COPY},
		{wx.wxACCEL_CTRL,string.byte('X'),wx.wxID_CUT},
		{wx.wxACCEL_CTRL,string.byte('V'),wx.wxID_PASTE},
		{wx.wxACCEL_NORMAL,wx.WXK_F7,ID"ToolPack"},
		{wx.wxACCEL_NORMAL,wx.WXK_F6,ID"ToolDebugStage"},
		{wx.wxACCEL_SHIFT,wx.WXK_F6,ID"ToolDebugSC"},
		{wx.wxACCEL_NORMAL,wx.WXK_F5,ID"ToolRun"},
	})
	frame:SetAcceleratorTable(accelTable)
	--
	frame:Connect(ID"ToolNew",wx.wxEVT_COMMAND_TOOL_CLICKED,NewProj)
	frame:Connect(ID"ToolOpen",wx.wxEVT_COMMAND_TOOL_CLICKED,OpenProj)
	frame:Connect(ID"ToolSave",wx.wxEVT_COMMAND_TOOL_CLICKED,SaveProj)
	frame:Connect(ID"ToolMerge",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		local fd=wx.wxFileDialog(frame,"Open project",setting.projpath,"","LuaSTG project (*.luastg)|*.luastg",wx.wxFD_FILE_MUST_EXIST+wx.wxFD_OPEN)
		if fd:ShowModal()==wx.wxID_OK then
			local fileName=fd:GetPath()
			local msg=LoadFromFile(fileName,true)
			if msg==nil then
				TreeShotUpdate()
				projectTree:SetFocus()
			else
				OutputLog(msg,"Error")
				return msg
			end
		end
	end)
	--
	frame:Connect(ID"ToolSetting",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		resXText:SetValue(tostring(setting.resx))
		resYText:SetValue(tostring(setting.resy))
		windowedCheckBox:SetValue(setting.windowed)
		updateLibCheckBox:SetValue(setting.updatelib)
		dialogSetting:Show(true)
	end)
	dialogSetting:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		if tonumber(resXText:GetValue())==nil or tonumber(resYText:GetValue())==nil then wx.wxMessageBox("Resolution must be number","Error",wx.wxICON_ERROR) return end
		setting.resx=tonumber(resXText:GetValue())
		setting.resy=tonumber(resYText:GetValue())
		setting.windowed=windowedCheckBox:GetValue()
		setting.updatelib=updateLibCheckBox:GetValue()
		local f=io.open("editor\\EditorSetting.lua","w")
		f:write("setting="..Tree.Serialize(setting))
		f:close()
		event:Skip()
	end)
	frame:Connect(ID"ToolPack",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		isDebug=false
		debugNode=nil
		scDebugNode=nil
		local msg=PackProj(event)
		if msg~=nil then
			OutputLog(msg,"Error")
		end
	end)
	frame:Connect(ID"ToolRun",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		if curProjFile then
			isDebug=false
			debugNode=nil
			scDebugNode=nil
			local msg=PackProj(event)
			if msg==nil then
				os.execute(string.format('start /b .\\LuaSTG.exe "start_game=true setting.nosplash=true setting.windowed=%s setting.resx=%s setting.resy=%s updatelib=%s setting.mod=\'%s\'"',
					tostring(setting.windowed),tostring(setting.resx),tostring(setting.resy),tostring(setting.updatelib),outputName))
			else
				OutputLog(msg,"Error")
			end
		end
	end)
	frame:Connect(ID"ToolDebugStage",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		if curProjFile then
			isDebug=true
			debugNode=nil
			scDebugNode=nil
			local msg=PackProj(event)
			if msg==nil then
				os.execute(string.format('start /b .\\LuaSTG.exe "start_game=true setting.nosplash=true setting.windowed=%s setting.resx=%s setting.resy=%s updatelib=%s setting.mod=\'%s\'"',
					tostring(setting.windowed),tostring(setting.resx),tostring(setting.resy),tostring(setting.updatelib),outputName))
			else
				OutputLog(msg,"Error")
			end
		end
	end)
	frame:Connect(ID"ToolDebugSC",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event)
		if curProjFile then
			isDebug=false
			debugNode=nil
			scDebugNode=curNode
			if Tree.data[scDebugNode:GetValue()]['type']~='bossspellcard' then
				OutputLog('current node is not a spell card node',"Error")
			else
				local msg=PackProj(event)
				if msg==nil then
					os.execute(string.format('start /b .\\LuaSTG.exe "start_game=true setting.nosplash=true setting.windowed=%s setting.resx=%s setting.resy=%s updatelib=%s setting.mod=\'%s\'"',
						tostring(setting.windowed),tostring(setting.resx),tostring(setting.resy),tostring(setting.updatelib),outputName))
				else
					OutputLog(msg,"Error")
				end
			end
		end
	end)
	local function CountEnter(s)
		local _,n=string.gsub(s,"\n","\n")
		lineNum=lineNum-n
	end
	local function FindNode(node)
		local data=Tree.data[node:GetValue()]
		local head=nodeType[data.type].tohead
		local foot=nodeType[data.type].tofoot
		if head then CountEnter(head(data)) end
		if lineNum<=0 then projectTree:SelectItem(node) return true end
		for child in Tree.Children(projectTree,node) do
			if FindNode(child) then return true end
		end
		if foot then CountEnter(foot(data)) end
		if lineNum<=0 then projectTree:SelectItem(node) return true end
	end
	local function GoToLineNum()
		if curProjFile then
			lineNum=tonumber(lineNumText:GetValue())
			if lineNum==nil then
				wx.wxMessageBox("Must input an integer","Error",wx.wxICON_ERROR)
				lineNumText:SetValue("")
				return
			end
			for child in Tree.Children(projectTree,rootNode) do
				if FindNode(child) then projectTree:SetFocus() return end
			end
			wx.wxMessageBox("End of project is reached","Info",wx.wxICON_INFORMATION)
		end
	end
	frame:Connect(ID"ToolFind",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event) GoToLineNum() end)
	lineNumText:Connect(wx.wxEVT_COMMAND_TEXT_ENTER,function(event) GoToLineNum() end)
	--
	frame:Connect(ID"ToolDelete",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolDelete)
	frame:Connect(ID"ToolCopy",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolCopy)
	frame:Connect(ID"ToolCut",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolCut)
	frame:Connect(ID"ToolPaste",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolPaste)
	frame:Connect(ID"ToolUndo",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolUndo)
	frame:Connect(ID"ToolRedo",wx.wxEVT_COMMAND_TOOL_CLICKED,ToolRedo)
	--
	frame:Connect(ID"ToolInsertAfter",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event) insertPos="after" end)
	frame:Connect(ID"ToolInsertBefore",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event) insertPos="before" end)
	frame:Connect(ID"ToolInsertChild",wx.wxEVT_COMMAND_TOOL_CLICKED,function(event) insertPos="child" end)
	--
	dialogNewProj:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		local fileName=filePickerNewProj:GetPath()
		if fileName=="" then wx.wxMessageBox("Specify file path and name first","Error",wx.wxICON_ERROR) return end
		local msg=LoadFromFile("editor\\templates\\"..templates[listTemplate:GetSelection()+1][2])
		if msg==nil then
			local msg2=SaveToFile(fileName)
			if msg2==nil then
				projectTree:Expand(rootNode)
				SetCurProjFile(fileName)
				frame:Enable(true)
				TreeShotUpdate()
				savedPos=1
				event:Skip()
			else
				Tree.data={}
				projectTree:DeleteChildren(rootNode)
				OutputLog(msg2,"Error")
			end
		else
			OutputLog(msg,"Error")
		end
	end)
	dialogNewProj:Connect(wx.wxID_CANCEL,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		frame:Enable(true)
		event:Skip()
	end)
	local function EditAttr(event)
		local data=Tree.data[curNode:GetValue()]
		if data then
			local enum=nodeType[data.type][attrIndex][2]
			if picker[enum] then
				picker[enum]:Show(true)
			elseif enum=="resfile" then
				local wildCard
				if data.type=='loadsound' or data.type=='loadbgm' then wildCard="Audio file (*.wav;*.mp3;*.ogg)|*.wav;*.mp3;*.ogg"
				elseif data.type=='loadimage' or data.type=='loadani' or data.type=='bossdefine' then wildCard="Image file (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp"
				elseif data.type=='loadparticle' then wildCard="Particle system info file (*.psi)|*.psi"
				elseif data.type=='patch' then wildCard="Lua file (*.lua)|*.lua"
				else wildCard="All types (*.*)|*.*" end
				local fd=wx.wxFileDialog(frame,"Select resource file",curProjDir,"",wildCard,wx.wxFD_FILE_MUST_EXIST+wx.wxFD_OPEN)
				if fd:ShowModal()==wx.wxID_OK then
					local fn=wx.wxFileName(fd:GetPath())
					if not fn:MakeRelativeTo(curProjDir) then
						OutputLog("It is recommended that resource file path is relative to project path.","Warning")
					end
					if data.type=='bossdefine' then
						attrCombo[5]:SetValue(fn:GetFullPath())
					elseif data.type=='patch' then
						attrCombo[1]:SetValue(fn:GetFullPath())
					else
						attrCombo[1]:SetValue(fn:GetFullPath())
						attrCombo[2]:SetValue(fn:GetName())
						if data.type=='loadparticle' then
							local f,msg=io.open(fd:GetPath(),'rb')
							if f==nil then OutputLog(msg,"Error")
							else
								local s=f:read(1)
								f:close()
								attrCombo[3]:SetValue('parimg'..(string.byte(s,1)+1))
							end
						end
					end
					SubmitAttr()
				end
			else
				dialogEditText:Show(true)
			end
		end
	end
	projectTree:Connect(wx.wxEVT_COMMAND_TREE_SEL_CHANGED,function(event)
		SubmitAttr()
		curNode=event:GetItem()
		if curNode:GetValue()~=rootNode:GetValue() then
			local data=Tree.data[curNode:GetValue()]
			typeNameLabel:SetLabel("Node type: "..nodeType[data["type"]].disptype)
			for i=1,#(data.attr) do
				attrLabel[i]:SetLabel(nodeType[data["type"]][i][1])
				attrCombo[i]:Clear()
				for _,v in ipairs(enumType[nodeType[data["type"]][i][2]]) do
					attrCombo[i]:Append(v)
				end
				attrCombo[i]:SetValue(data.attr[i])
				attrButton[i]:Enable(true)
				local enum=nodeType[data["type"]][i][2]
				if picker[enum] or enum=='resfile' then
					attrButton[i]:SetLabel("...")
					attrCombo[i]:Enable(true)
				else
					attrButton[i]:SetLabel(" . ")
					attrCombo[i]:Enable(true)
				end
			end
			for i=#(data.attr)+1,15 do
				attrLabel[i]:SetLabel("")
				attrCombo[i]:SetValue("")
				attrCombo[i]:Enable(false)
				attrButton[i]:Enable(false)
				attrButton[i]:SetLabel(" . ")
			end
			if data.attr[1]=="" and nodeType[data["type"]].editfirst then
				attrIndex=1
				EditAttr(event)
			end
		else
			typeNameLabel:SetLabel("Node type: project")
			for i=1,15 do
				attrLabel[i]:SetLabel("")
				attrCombo[i]:SetValue("")
				attrCombo[i]:Enable(false)
				attrButton[i]:Enable(false)
				attrButton[i]:SetLabel("...")
			end
		end
	end)
	projectTree:Connect(wx.wxEVT_COMMAND_TREE_DELETE_ITEM,function(event)
		local item_id=event:GetItem():GetValue()
		if nodeType[Tree.data[item_id]["type"]].watch then Tree.watch[nodeType[Tree.data[item_id]["type"]].watch][item_id]=nil end
		Tree.data[item_id]=nil
		event:Skip()
	end)
	projectTree:Connect(wx.wxEVT_KEY_DOWN,function(event)
		if event:GetKeyCode()==wx.WXK_RETURN and IsValid(curNode) then
			if #(Tree.data[curNode:GetValue()].attr)~=0 then
				attrIndex=1
				EditAttr(event)
			end
		end
		event:Skip()
	end)
	projectTree:Connect(wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,function(event)
		projectTree:SelectItem(event:GetItem())
		if IsValid(curNode) then
			if #(Tree.data[curNode:GetValue()].attr)~=0 then
				attrIndex=1
				EditAttr(event)
			end
		end
		event:Skip()
	end)
	--
	for i=1,15 do
		attrCombo[i]:Connect(wx.wxEVT_KEY_UP,function(event)
			if event:GetKeyCode()==wx.WXK_ESCAPE and IsValid(curNode) then
				local data=Tree.data[curNode:GetValue()]
				attrCombo[i]:SetValue(data.attr[i])
			end
		end)
		attrCombo[i]:Connect(wx.wxEVT_COMMAND_TEXT_ENTER ,function(event)
			SubmitAttr()
		end)
	end
	--
	for i=1,15 do
		attrButton[i]:Connect(wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
			attrIndex=i
			EditAttr(event)
		end)
	end
	--
	dialogEditText:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		attrCombo[attrIndex]:SetValue(editAttrText:GetValue())
		SubmitAttr()
		event:Skip()
	end)
	dialogEditText:Connect(wx.wxEVT_SHOW,function(event)
		if dialogEditText:IsShown() then
			editAttrLabel:SetLabel(attrLabel[attrIndex]:GetLabel())
			editAttrText:SetValue(attrCombo[attrIndex]:GetValue())
		end
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	local accelTable=wx.wxAcceleratorTable({
        {wx.wxACCEL_CTRL,wx.WXK_RETURN,wx.wxID_OK},
	})
	dialogEditText:SetAcceleratorTable(accelTable)
	--
	dialogSoundEffect:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		if soundListBox:GetStringSelection()~="" then attrCombo[attrIndex]:SetValue(soundListBox:GetStringSelection()) end
		SubmitAttr()
		event:Skip()
	end)
	dialogSoundEffect:Connect(wx.wxEVT_COMMAND_LISTBOX_SELECTED,function(event)
		local sel=soundListBox:GetStringSelection()
		if resList.snd[sel] then wx.wxSound.Play("data\\"..resList.snd[sel])
		else
			wx.wxSound.Play(MakeFullPath(soundList[sel]))
		end
		event:Skip()
	end)
	dialogSoundEffect:Connect(wx.wxEVT_SHOW,function(event)
		if dialogSoundEffect:IsShown() then
			soundListBox:Clear()
			for k,v in pairs(resList.snd) do
				soundListBox:Append(k)
			end
			soundList={}
			for k,v in pairs(Tree.watch.sound) do
				soundListBox:Append(Tree.data[k].attr[2])
				soundList[Tree.data[k].attr[2]]=Tree.data[k].attr[1]
			end
			soundListBox:SetStringSelection(attrCombo[attrIndex]:GetValue())
		end
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	--
	dialogType:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		if typeListBox:GetStringSelection()~="" then attrCombo[attrIndex]:SetValue(typeListBox:GetStringSelection()) end
		SubmitAttr()
		event:Skip()
	end)
	dialogType:Connect(wx.wxEVT_SHOW,function(event)
		if dialogType:IsShown() then
			typeListBox:Clear()
			local watch
			local t=Tree.data[curNode:GetValue()]["type"]
			if t=='enemycreate' then watch='enemydefine'
			elseif t=='bosscreate' then watch='bossdefine'
			elseif t=='bulletcreate' then watch='bulletdefine'
			elseif t=='objectcreate' then watch='objectdefine'
			elseif t=='lasercreate' then watch='laserdefine'
			elseif t=='bossdefine' then watch='bgdefine'
			else
				--
			end
			for k,v in pairs(Tree.watch[watch]) do
				typeListBox:Append(Tree.data[k].attr[1])
			end
			typeListBox:SetStringSelection(attrCombo[attrIndex]:GetValue())
		end
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	--
	dialogInputParameter:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		local plist={}
		for i=1,16 do
			if paramText[i]:IsEditable() then
				if paramText[i]:GetValue()=="" then table.insert(plist,"nil")
				else table.insert(plist,paramText[i]:GetValue()) end
			end
		end
		attrCombo[attrIndex]:SetValue(table.concat(plist,","))
		SubmitAttr()
		event:Skip()
	end)
	local function SplitParam(s)
		if string.match(s,"^[%s]*$") then return {} end
		local pos={0}
		local ret={}
		local b1=0 local b2=0
		for i=1,#s do
			local c=string.byte(s,i)
			if b1==0 and b2==0 and c==44 then table.insert(pos,i)
			elseif c==40 then b1=b1+1
			elseif c==41 then b1=b1-1
			elseif c==123 then b2=b2+1
			elseif c==125 then b2=b2-1
			end
		end
		table.insert(pos,#s+1)
		for i=1,#pos-1 do
			table.insert(ret,string.sub(s,pos[i]+1,pos[i+1]-1))
		end
		return ret
	end
	local function FindNodeByTypeName(node,tname)
		if _def_node_type[node.type] and node.attr[1]==tname then return node
		else
			local ret
			for k,v in pairs(node.child) do
				ret=FindNodeByTypeName(v,tname)
				if ret then return ret end
			end
		end
	end
	dialogInputParameter:Connect(wx.wxEVT_SHOW,function(event)
		if dialogInputParameter:IsShown() then
			local tname=Tree.data[curNode:GetValue()].attr[1]
			local plist=Tree.data[curNode:GetValue()].attr[attrIndex]
			local ret
			for k,v in pairs(treeShot[treeShotPos]) do
				ret=FindNodeByTypeName(v,tname)
				if ret then
					for k,v in pairs(ret.child) do
						if _init_node_type[v.type] then
							local ret2=SplitParam(v.attr[1])
							local ret3=SplitParam(plist)
							for i=1,#ret2 do
								paramNameLabel[i]:SetLabel(ret2[i])
								paramText[i]:SetValue(ret3[i] or "")
								paramText[i]:SetEditable(true)
							end
							for i=#ret2+1,16 do
								paramNameLabel[i]:SetLabel("")
								paramText[i]:SetValue("")
								paramText[i]:SetEditable(false)
							end
						end
					end
					break
				end
			end
		end
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	--
	dialogImage:Connect(wx.wxID_OK,wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		if imageListBox:GetStringSelection()~="" then attrCombo[attrIndex]:SetValue(imageListBox:GetStringSelection()) end
		SubmitAttr()
		event:Skip()
	end)
	local function ImagePrevPanelPaint(event)
		local sel=imageListBox:GetStringSelection()
		local panel_dc=wx.wxClientDC(imagePrevPanel)
		panel_dc:DrawBitmap(bitmapBackground,0,0,false)
		if imageList[sel] then
			local cur_bmp=wx.wxBitmap(MakeFullPath(imageList[sel]))
			panel_dc:DrawBitmap(cur_bmp,128-cur_bmp:GetWidth()/2,128-cur_bmp:GetHeight()/2,true)
		end
		event:Skip()
	end
	dialogImage:Connect(wx.wxEVT_COMMAND_LISTBOX_SELECTED,function(event)
		ImagePrevPanelPaint(event)
	end)
	dialogImage:Connect(wx.wxEVT_PAINT,function(event)
		ImagePrevPanelPaint(event)
	end)
	dialogImage:Connect(wx.wxEVT_SHOW,function(event)
		if dialogImage:IsShown() then
			local imgonly=false
			if Tree.data[curNode:GetValue()]["type"]=='loadparticle' or Tree.data[curNode:GetValue()]["type"]=='bglayer' then imgonly=true end
			imageListBox:Clear()
			imageList={}
			if imgonly then
				for k,v in pairs(Tree.watch.image) do
					if Tree.data[k]["type"]=='loadimage' then
						imageListBox:Append('image:'..Tree.data[k].attr[2])
						imageList['image:'..Tree.data[k].attr[2]]=Tree.data[k].attr[1]
					end
				end
			else
				for k,v in pairs(Tree.watch.image) do
					if Tree.data[k]["type"]=='loadimage' then
						imageListBox:Append('image:'..Tree.data[k].attr[2])
						imageList['image:'..Tree.data[k].attr[2]]=Tree.data[k].attr[1]
					elseif Tree.data[k]["type"]=='loadani' then
						imageListBox:Append('ani:'..Tree.data[k].attr[2])
						imageList['ani:'..Tree.data[k].attr[2]]=Tree.data[k].attr[1]
					elseif Tree.data[k]["type"]=='loadparticle' then
						imageListBox:Append('particle:'..Tree.data[k].attr[2])
					end
				end
			end
			imageListBox:SetStringSelection(attrCombo[attrIndex]:GetValue())
		end
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	--
	dialogSelectEnemyStyle:Connect(wx.wxEVT_SHOW,function(event)
		frame:Enable(not frame:IsEnabled())
		event:Skip()
	end)
	for i=1,26 do
		selectEnemyStyleButton[i]:Connect(wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
			dialogSelectEnemyStyle:Hide(true)
			attrCombo[attrIndex]:SetValue(tostring(i))
			SubmitAttr()
			event:Skip()
		end)
	end
	--
	frame:Connect(wx.wxEVT_COMMAND_BUTTON_CLICKED,function(event)
		local nodeType=Id2Type[event:GetId()]
		if nodeType and (IsValid(curNode) or IsRoot(curNode)) then
			local data
			if nodeType.default then data=nodeType.default
			else
				data={["type"]=nodeType.name,attr={}}
				for i=1,#(nodeType) do
					data.attr[i]=nodeType[i][4] or enumType[nodeType[i][2]][1] or ""
				end
			end
			InsertNode(projectTree,curNode,data)
		end
	end)
	--
	frame:CreateStatusBar()
	frame:GetStatusBar():SetStatusText("Ready.")
	--
	frame:Center()
	frame:Maximize(true)
	frame:Show(true)
end

main()
wx.wxGetApp():MainLoop()
